package de.mmis.core.infrastructure.gui;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import javax.swing.JTable;
import javax.swing.RowFilter;

import de.mmis.core.base.abstracttree.InnerNode;
import de.mmis.core.base.abstracttree.LeafNode;
import de.mmis.core.base.abstracttree.Tree;
import de.mmis.core.base.abstracttree.TreeParserException;
import de.mmis.core.base.abstracttree.serializer.DeserializableException;
import de.mmis.core.base.infrastructure.PublishedDeviceDescription;
import de.mmis.core.base.infrastructure.PublishedDeviceDescription.PublishType;
import de.mmis.core.sexpression.SExpressionParser;

/**
 * Class filtering {@link PublishedDeviceDescription} for a {@link JTable}. It
 * takes a {@link String}. If the String is an {@link Tree} wich can be
 * converted to a {@link PublishedDeviceDescription}, it is used as a pattern.<br/>
 * If it can't be converted, this class tries to use it as a RegEx. If this also
 * fails, it is used as a simple String pattern.
 * 
 * @author Martin Nyolt
 * 
 */
class DeviceFilter extends RowFilter<DeviceTableModel, Integer> {

	private PublishedDeviceDescription devicePattern;
	private Pattern regexPattern;
	private String stringPattern;
	private List<Object> infoKeys;

	private static <T> T deAst(Tree e, Class<T> clazz)
			throws DeserializableException {
		if (new LeafNode("?").equals(e))
			return null;

		return e.getAs(clazz);
	}

	/**
	 * 
	 * @param s
	 *            The pattern to be used.
	 */
	public DeviceFilter(String s, List<Object> infoKeys) {
		devicePattern = null;
		regexPattern = null;
		stringPattern = null;

		if (infoKeys == null)
			infoKeys = new LinkedList<Object>();
		this.infoKeys = infoKeys;

		try {
			Tree expr = SExpressionParser.parseUTF8(s);
			if (expr instanceof InnerNode) {
				InnerNode p = (InnerNode) expr;
				if (p.getNumberOfSubTrees() == 6) {
					String id = deAst(p.getSubTree(0), String.class);

					String deviceHost = deAst(p.getSubTree(1),
							String.class);

					PublishType type = deAst(p.getSubTree(2),
							PublishType.class);

					String hostIP = deAst(p.getSubTree(3), String.class);

					Integer Port = deAst(p.getSubTree(4), Integer.class);
					int port = Port == null ? -1 : Port.intValue();

					@SuppressWarnings("unchecked")
					Map<String, Object> properties = deAst(
							p.getSubTree(5), Map.class);

					devicePattern = new PublishedDeviceDescription(id,
							deviceHost, type, hostIP, port, properties);

					System.err.println(devicePattern);
				}
			}
		} catch (TreeParserException e) {
			// ignore
		} catch (DeserializableException e) {
			// ignore
		}

		if (devicePattern != null)
			return;

		try {
			regexPattern = Pattern.compile(s);
		} catch (PatternSyntaxException e) {
			// ignore
		}

		if (regexPattern != null)
			return;

		stringPattern = s;
	}

	@Override
	public boolean include(
			javax.swing.RowFilter.Entry<? extends DeviceTableModel, ? extends Integer> entry) {

		PublishedDeviceDescription device = entry.getModel().getDevice(
				entry.getIdentifier().intValue());

		// first check if all keys are present
		Map<String, Object> properties = device.getProperties();
		if (!properties.keySet().containsAll(infoKeys))
			return false;

		// if we have a devicePattern use it

		if (devicePattern != null)
			return device.matches(devicePattern);

		LinkedList<String> strings = new LinkedList<String>();
		strings.add(device.getID().toString());
		strings.add(device.getDeviceHost());
		strings.add(device.getType().toString());
		strings.add(device.getHost().toString());
		strings.add(Integer.toString(device.getPort()));

		// otherwise try each entry to match the RegEx

		if (regexPattern != null) {
			for (String s : strings)
				if (regexPattern.matcher(s).find())
					return true;
			return false;
		}

		// otherwise at least one entry should contain the stringPattern

		for (String s : strings)
			if (s.contains(stringPattern))
				return true;

		return false;
	}
}
